package org.duiduo.common.tool.util;

import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.IOUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 切割文件工具
 *
 * @author liangze
 * @date 2024/11/4 15:43
 */
@Slf4j
public class CutFileUtil {

    /**
     * 切割文件
     *
     * @param filePath        文件所在主机的路径 例：/home/gyt/nginx.tar
     * @param actualBlockSize 拆分文件字节大小
     * @param saveAddr        拆分后的文件保存目录  /homt/gyt/
     * @return 切割后的文件名集合
     */
    public List<String> cutFileBySize(String filePath, Long actualBlockSize, String saveAddr) {
        List<String> fileNames = new ArrayList<>();
        File file = new File(filePath);
        //计算总共段数
        int count = (int) Math.ceil(file.length() / (double) actualBlockSize);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(count * 2));
        try {
            for (int i = 0; i < count; i++) {
                //分段文件名
                String fileName = FileUtil.getPrefix(file) + "_" + String.format("%02d", i + 1) + "." + FileUtil.extName(file);
                Long startPos = i * actualBlockSize;
                threadPoolExecutor.execute(() -> splitFile(actualBlockSize, fileName, file, startPos, saveAddr));
                fileNames.add(fileName);
            }
        } catch (Exception e) {
            log.error("文件切割异常:", e);
            throw new RuntimeException(e.getMessage());
        } finally {
            threadPoolExecutor.shutdown();
        }
        // 主线程等待线程执行完成
        while (true) {
            if (threadPoolExecutor.isTerminated()) {
                return fileNames;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 切割文件处理
     *
     * @param actualBlockSize 切割长度
     * @param fileName        原文件名
     * @param originFile      切割后文件名
     * @param startPos        每“段”开头
     * @param currentWorkDir  切割目录
     */
    private void splitFile(long actualBlockSize, String fileName, File originFile, Long startPos, String currentWorkDir) {
        RandomAccessFile raf = null;
        BufferedOutputStream bos = null;
        try {
            raf = new RandomAccessFile(originFile, "r");
            bos = new BufferedOutputStream(new FileOutputStream(currentWorkDir + fileName));

            raf.seek(startPos); //移动指针到每“段”开头
            byte[] flush = new byte[1024 * 10];

            int len = 0;
            while (-1 != (len = raf.read(flush))) {
                if (actualBlockSize - len >= 0) { //查看是否足够
                    //写出
                    bos.write(flush, 0, len);
                    actualBlockSize -= len; //剩余量
                } else { //写出最后一次的剩余量
                    bos.write(flush, 0, (int) actualBlockSize);
                    break;
                }
            }
        } catch (FileNotFoundException e) {
            log.error("文件不存在异常:", e);
        } catch (IOException e) {
            log.error("文件切割异常:", e);
        } finally {
            IOUtils.closeQuietly(bos);
            IOUtils.closeQuietly(raf);
        }
    }
}

